﻿package kit4j.ui.cs.swing.full;

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;

import javax.swing.Icon;
import javax.swing.JLabel;

import kit4j.ui.cs.full.BackgroundConstraints;
import kit4j.ui.cs.full.FullComponent;
import kit4j.ui.cs.full.FullComponentHelper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 实现了FullComponent接口的继承自JLabel的类型。
 * 
 * @author MiniKnife
 * 
 */
public class JFullLabel extends JLabel implements FullComponent {

	private static final long serialVersionUID = 1L;

	private static final Logger log = LoggerFactory.getLogger(JFullLabel.class);

	private boolean autoChangeFontSize;

	private boolean insertHtml = false;

	private boolean ignoreRepaint = false;

	private Object ignoreRepaintLock;

	private Font standardFont;

	public JFullLabel() {
		super();
	}

	public JFullLabel(String text) {
		super(text);
	}

	public JFullLabel(Icon image) {
		super(image);
	}

	public JFullLabel(String text, int horizontalAlignment) {
		super(text, horizontalAlignment);
	}

	public JFullLabel(Icon image, int horizontalAlignment) {
		super(image, horizontalAlignment);
	}

	public JFullLabel(String text, Icon icon, int horizontalAlignment) {
		super(text, icon, horizontalAlignment);
	}

	private FullComponentHelper fullComponentHelper = new FullComponentHelper(this);

	@Override
	public void setBackgroundImage(Image img) {
		this.fullComponentHelper.setBackgroundImage(img);
	}

	@Override
	public Image getBackgroundImage() {
		return this.fullComponentHelper.getBackgroundImage();
	}

	@Override
	public void paintComponent(Graphics g) {
		if (isAutoChangeFontSize()) {
			synchronized (ignoreRepaintLock) {
				if (ignoreRepaint) {
					return;
				} else {
					ignoreRepaint = true;
				}
			}

			changeFontSize(g, false);

			synchronized (ignoreRepaintLock) {
				ignoreRepaint = false;
			}
		}

		this.fullComponentHelper.paintBackgroundImage(g);
		super.paintComponent(g);
	}

	@Override
	public BackgroundConstraints getBackgroundConstraints() {
		return this.fullComponentHelper.getBackgroundConstraints();
	}

	@Override
	public void setBackgroundConstraints(BackgroundConstraints bgc) {
		this.fullComponentHelper.setBackgroundConstraints(bgc);
	}

	@Override
	public void setAutoGenerateComponentsBackgroundImage(boolean autoGenerateComponentsBackgroundImage) {
		this.fullComponentHelper.setAutoGenerateComponentsBackgroundImage(autoGenerateComponentsBackgroundImage);
	}

	@Override
	public boolean isAutoGenerateComponentsBackgroundImage() {
		return this.fullComponentHelper.isAutoGenerateComponentsBackgroundImage();
	}

	/**
	 * @see javax.swing.JComponent#setFont(java.awt.Font)
	 */
	@Override
	public void setFont(Font font) {
		setFont(font, true);
	}

	/**
	 * 设置组件使用的字体。
	 * 
	 * @param font
	 *            要设置的字体
	 * @param isStandardFont
	 *            是否设置为标准字体(用于开启自动改变字体尺寸功能时)
	 * @see javax.swing.JComponent#setFont(java.awt.Font)
	 */
	public void setFont(Font font, boolean isStandardFont) {
		super.setFont(font);

		if (isAutoChangeFontSize() && isStandardFont) {
			this.standardFont = font;
		}
	}

	/**
	 * @return the autoChangeFontSize
	 */
	public boolean isAutoChangeFontSize() {
		return autoChangeFontSize;
	}

	/**
	 * @param autoChangeFontSize
	 *            the autoChangeFontSize to set
	 */
	public void setAutoChangeFontSize(boolean autoChangeFontSize) {
		this.autoChangeFontSize = autoChangeFontSize;

		if (autoChangeFontSize) {
			if (ignoreRepaintLock == null) {
				ignoreRepaintLock = new Object();
			}
		}
	}

	/**
	 * @return the standardFont
	 */
	public Font getStandardFont() {
		return standardFont;
	}

	/**
	 * @param standardFont
	 *            the standardFont to set
	 */
	public void setStandardFont(Font standardFont) {
		setFont(standardFont, true);
	}

	protected void changeFontSize(Graphics g) {
		String text = getText();

		if (insertHtml) {
			text = text.replaceFirst("<html>", "");
			text = text.replaceFirst("</html>", "");
			text = text.replaceAll("<br />", "");

			setText(text);
		}

		final int textLength = text.length();
		final Font font = standardFont;
		final int fontSize = font.getSize();
		final Insets insets = getInsets();
		final int width = getWidth() - insets.left - insets.right;
		final int height = getHeight() - insets.top - insets.bottom;
		float newFontSize = 0;

		FontMetrics metrics = g.getFontMetrics(font);
		LineMetrics lineMetrics = metrics.getLineMetrics(text, g);
		int textWidth = metrics.stringWidth(text);
		float textHeigth = lineMetrics.getHeight();
		float columnSize = width / textLength - 1;
		float rowSize = height;
		if (textWidth > width) {
			float xSize = columnSize;
			float ySize = rowSize;
			int tempSize = 0;
			int rowTextLength = 0;
			int oddTextLength = 0;
			int rowTextCount = 0;
			int rowCount = 0;

			for (int row = 2; ySize > xSize; ++row) {
				tempSize = (3 * height) / (4 * row + 1);

				log.trace("row={}, xSize={}, ySize={}, tempSize{}", new Object[] { row, xSize, ySize, tempSize });

				if (tempSize > fontSize) {
					tempSize = fontSize;
				}

				if (tempSize <= xSize) {
					newFontSize = xSize;
					break;
				} else {
					ySize = tempSize;
					rowTextLength = (textLength + row - 1) / row;

					if (rowTextLength * (row - 1) > textLength) {
						continue;
					}

					oddTextLength = textLength % rowTextLength;

					if (oddTextLength == 0) {
						oddTextLength = rowTextLength;
					}

					for (int i = oddTextLength / (row - 1); i >= 0; --i) {
						rowTextCount = rowTextLength + i;
						xSize = width / rowTextCount - 1;

						log.trace(
								"row={}, rowTextLength={}, oddTextLength={}, i={}, rowTextCount={}, xSize={}, textLength={}, fontSize={}",
								new Object[] { row, rowTextLength, oddTextLength, i, rowTextCount, xSize, textLength,
										fontSize });

						if (xSize >= ySize) {
							break;
						}
					}
				}

				rowCount = row;
			}

			if (ySize <= xSize) {
				newFontSize = xSize;
			}

			if (rowTextCount < textLength) {
				log.trace("rowCount={}, rowTextCount={}, xSize={}, ySize={}, newFontSize={}, fontSize={}",
						new Object[] { rowCount, rowTextCount, xSize, ySize, newFontSize, fontSize });

				StringBuilder builder = new StringBuilder();
				builder.append("<html>");

				for (int i = 1; i < rowCount; ++i) {
					builder.append(text.substring(rowTextCount * (i - 1), rowTextCount * i));
					builder.append("<br />");
				}

				builder.append(text.substring(rowTextCount * (rowCount - 1)));
				builder.append("</html>");

				setText(builder.toString());

				insertHtml = true;
			}
		} else if (textHeigth > height) {
			newFontSize = height;
		}

		if (newFontSize != 0 && newFontSize < fontSize) {
			setFont(font.deriveFont(newFontSize), false);
		}
	}

	protected void changeFontSize(Graphics g, boolean enableBranch) {
		String text = getText();

		if (insertHtml) {
			text = text.replaceFirst("<html>", "");
			text = text.replaceFirst("</html>", "");
			text = text.replaceAll("<br />", "");

			setText(text);
		}

		final Insets insets = getInsets();
		final int width = getWidth() - insets.left - insets.right;
		final int height = getHeight() - insets.top - insets.bottom;
		final int textLength = text.length();
		final Font font = standardFont;
		float newFontSize = font.getSize2D();
		Rectangle2D bounds = getStringBounds(text, g, font);

		if (!enableBranch) {
			if (bounds.getWidth() > width) {
				newFontSize = (float) (newFontSize * width / bounds.getWidth());
				Font tempFont = font.deriveFont(newFontSize);
				Rectangle2D tempBounds = getStringBounds(text, g, tempFont);
				bounds = tempBounds;

				if (tempBounds.getWidth() >= width) {
					while (true) {
						--newFontSize;
						tempFont = font.deriveFont(newFontSize);
						tempBounds = getStringBounds(text, g, tempFont);
						bounds = tempBounds;
						
						if(tempBounds.getWidth() < width) {
							break;
						}
					}
				} else if (tempBounds.getWidth() < width) {
					while (true) {
						tempFont = font.deriveFont(newFontSize + 1);
						tempBounds = getStringBounds(text, g, tempFont);
						
						if(tempBounds.getWidth() < width) {
							bounds = tempBounds;
							++newFontSize;
						} else {
							break;
						}
					}
				}
			}

			log.debug("width={},strWidth={},newFontSize={}", new Object[] { width, bounds.getWidth(), newFontSize });

			if (bounds.getHeight() > height) {
				newFontSize = (float) (newFontSize * height / bounds.getHeight());
				Font tempFont = font.deriveFont(newFontSize);
				Rectangle2D tempBounds = getStringBounds(text, g, tempFont);

				if (tempBounds.getHeight() >= height) {
					for (--newFontSize; tempBounds.getHeight() > height; --newFontSize) {
						tempFont = font.deriveFont(newFontSize);
						tempBounds = getStringBounds(text, g, tempFont);
					}
				} else if (tempBounds.getHeight() < height) {
					for (++newFontSize; tempBounds.getHeight() < height; ++newFontSize) {
						tempFont = font.deriveFont(newFontSize);
						tempBounds = getStringBounds(text, g, tempFont);
					}

					--newFontSize;
				}
			}
		} else if (bounds.getWidth() > width) {

		}

		if (newFontSize <= font.getSize2D() && newFontSize != getFont().getSize2D()) {
			setFont(font.deriveFont(newFontSize), false);
		}
	}

	private Rectangle2D getStringBounds(String str, Graphics g, Font font) {
		FontMetrics tempMetrics = g.getFontMetrics(font);

		return tempMetrics.getStringBounds(str, g);
	}

}
